home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 3 / Cream of the Crop 3.iso / clipper / ks94an.zip / LINEDGET.HDR < prev    next >
Text File  |  1994-04-25  |  17KB  |  384 lines

  1. /******************************************************************************
  2.                  The Klipper Library, for CA-Clipper 5.x
  3.         Copyright (c), 1994, Wallace Information Systems Engineering
  4.  
  5. FUNCTION:
  6.  
  7. _LineEditGet( cKey, cAlias, cField ) --> cString
  8.  
  9. PARAMETERS:
  10.  
  11. cKey   : Index Key Value for Text-Comment File
  12. cAlias : Text-Comment File Alias
  13. cField : Field name in the cAlias work area for text
  14.  
  15. SHORT:
  16.  
  17. Retrieve text from LineEdit comment database.
  18.  
  19. DESCRIPTION:
  20.  
  21. In the following discussion, it is assumed that you understand the basic
  22. principle by which MEMO fields operate, as well as their inherent
  23. inefficiency.
  24.  
  25. The LineEdit comments manager is a collection of functions that allow you to
  26. replace the inefficent MEMO field (*.DBT) with a more efficient
  27. database-based free-form text storage recycling system.  I say "recycling"
  28. because at the core of these functions lies an implementation of an already
  29. well known technique: that of reusing the space previously occupied by
  30. deleted records for new records, and appending new records only when there
  31. are no recyclable records available.
  32.  
  33. In a nutshell, there are five functions that allow you to store,
  34. retrieve, check the size of a comment, and check available storage
  35. capacity, for comments or any free-form text.  These functions are
  36. discussed next.
  37.  
  38. There is a complete demonstration of these functions under DEMOS\LINEEDIT,
  39. along with some realistic, (albeit unscientific) comparisons of LineEdit
  40. databases with conventional MEMO fields. The results may suprise you.
  41.  
  42. Another good example is the DEMOS\UTILS\LE_RESIZ\LERESIZE.PRG program, which
  43. will enable you to resize a comment base AFTER it has been created and used.
  44.  
  45. *****************************************************************************
  46.  
  47. _LineEditGet() retrieves text from the database and field named in cAlias
  48. and cField for all records matching cKey.
  49.  
  50. The database MUST be open and aliased by the name in cAlias, although it need
  51. not be the current work area.  All of these functions select the file by the
  52. alias specified in cAlias and restore whatever the current database was
  53. before the call was made.
  54.  
  55.  
  56. If no records match cKey, then the function returns an empty string.
  57. Test with EMPTY().  To test the length of the RETURNED string use LEN().
  58. To test the size of the comment BEFORE extracting it, use
  59. _LineEditSize(), which will return the size of the comment for the
  60. specified key and let you decide whether to _LineEditGet() it or not.
  61.  
  62. *****************************************************************************
  63.  
  64.  
  65. _LineEditPut() writes to the text database.  Comments kept, created,
  66. and written with _LineEditPut() are encrypted (and decrypted
  67. by _LineEditGet()).  In addition, records no longer in use are recycled
  68. for use in other comments.  This occurs when a text note is deleted or
  69. reduced in size.  The extra space that was once used by the text is freed
  70. to be used by another comment.
  71.  
  72. To recycle a record, chr(219) is replicated throughout the length of the
  73. key field and SEEK'd before APPENDING any more records. This accomplishes
  74. two things.  First, speed is increased by not ALWAYS having to append
  75. and write new records as new comments are added, but rather existing
  76. records are recycled (if available) and second, and probably most notable,
  77. the disk storage needed to store text is GREATLY diminished in the
  78. most commonplace circumstances.
  79.  
  80.  
  81. NOTE then, that you cannot use chr(219) as an index key for the note file
  82. or, subsequently, the database that the text is attached to.  A chr(219)
  83. can occur in the key, but the key cannot be entirely composed of them or
  84. the record will be taken as an unused record, available to be overwritten
  85. by another comment.
  86.  
  87. Any attempt to SEEK a null string (empty() or otherwise) will
  88. immediately result in "" as a return value.  This is a safety
  89. precaution because a SEEK on a null string may match EVERYTHING in the
  90. note database (depending on the setting of "SET EXACT").
  91.  
  92. *****************************************************************************
  93.  
  94. _LineEditSize() returns the size of a comment that is stored in a
  95. LineEdit database.  This may a necessary step in cases where comments
  96. may approach Clipper's 64K string length limit.
  97.  
  98. Theoritically, a comment stored in a LineEdit database may be of any size
  99. limited only by disk space.  However, any ability to manipulate this comment
  100. as a single text string is limited to 64K bytes by Clipper.
  101.  
  102. Given that the database must be written by _LineEditPut() in order to be
  103. retrievable by _LineEditGet(), this is very unlikely if at all possible.
  104.  
  105. However, if you intend to add functionality to your application by having
  106. your own routines add to the database (this is possible if you have the
  107. source code to these functions), then you would do well to have the ability
  108. to check the size of the stored comment before attempting to extract it with
  109. _LineEditGet().  In other cases, you may simply want to know ahead of time
  110. the size of the comment field before actually getting it.  On some 80286, and
  111. especially on any XT system (and I won't even mention PC's), the time to
  112. extract the comment may cause a noticeable pause which you may want to
  113. prepare your user for.
  114.  
  115. The size returned by _LineEditSize() includes some non-printable
  116. characters, such as CRLF pairs and "soft returns."  These characters are
  117. not seen during memoedit or most other editing mechanism as they
  118. are interpreted by most of them as formatting characters.  However, when
  119. they are converted to a disk file or database field, they are present and
  120. add to the overall length of the string.
  121.  
  122. Generally speaking, you should add two bytes for each line of text to
  123. cover these formatting characters.
  124.  
  125. Most text editing mechanisms, given the following, would report 18 bytes:
  126.  
  127. <top of string/file>
  128. ABCDEFG
  129. HIJKLMN
  130. <end>
  131.  
  132. because in addition to the 14 characters, there are two CRLF pairs at the
  133. end of each line which occupy two bytes each.
  134.  
  135. *****************************************************************************
  136.  
  137. _LineEditAvail() returns the number of bytes of comment storage space
  138. that exist in recycleable records.  This is the number of bytes of
  139. storage available without increasing the size of the database.  In a
  140. comment editing system that averages about the same number of bytes added
  141. as bytes deleted, usually about 50% of the size of the database will
  142. be available.  On systems where comments are only added and never
  143. deleted, then this function will return "0 bytes available."  Even in
  144. this case, the efficiency is better than plain MEMO fields which still
  145. grow exponentially even when you are repeatedly adding text. When comments
  146. are frequently edited, the database will contain more recycleable space.
  147.  
  148. *****************************************************************************
  149.  
  150. _LineEditKill() deletes records from the comment database by overwritting
  151. the key and comment fields of the comment database with a special
  152. character, effectively deleting them while leaving them in the file to
  153. be recycled for other comments.
  154.  
  155. *****************************************************************************
  156.  
  157. HOW TO MAKE A LINEEDIT DATABASE FOR FREE-FORM COMMENTS:
  158.  
  159. Let's say you have a database (COMPANY.DBF) with the following structure,
  160. with which you wish to keep free-form text comments:
  161.  
  162. COMPANY.DBF:
  163.  
  164. 1 COMP_NAME, C, 35
  165. 2 COMP_ACCT, C, 10
  166. ...
  167. ...
  168. ...
  169. n ...
  170.  
  171. Let's also suppose that the database's primary key index is on Company
  172. Account Number (COMP_ACCT).  To attach LineEdit text/comments to this
  173. database, you will need to create a database that has at least two fields:
  174. COMP_ACCT and a comment field.  You choose the name of the field, and it's
  175. length.  The minimum length is 30 bytes.  70 to 100 or more is recommended.
  176.  
  177. Generally speaking, there is a balance point between the size of the
  178. comment field and the speed of the LineEdit functions.  On the one hand, a
  179. larger comment field holds more data, decreasing the number of records needed
  180. to store a large comment, resulting in faster processing.  On the other hand,
  181. a larger comment field will result in more "white space" being stored, and is
  182. thus less space efficient.  Again, 70 to 100 bytes or more is recommended.
  183. It depends on the nature of the comments you are keeping. If you are
  184. typically adding one-line comments of 80 chars or so, then 90 chars is
  185. probably best.  The KLIPDOCS program uses 40 or 80 bytes for the comment base
  186. fields for the smaller texts (Function name and Parameters) and 254 for the
  187. larger ones (Description and Notes, etc).
  188.  
  189.  
  190. Create the LineEdit database with any name you like.  You will
  191. specifically refer to it later by the alias under which you open it. Let's
  192. say we decide to call it "SALENOTE.DBF."
  193.  
  194. Create your database with at least the COMP_ACCT field identical to the
  195. same field in the primary database (for relational indexing) and the
  196. comment field.  Let's say you decide to call it SALE_NOTES, C, 80.
  197.  
  198. Other fields unrelated to the text storage can be added for your own
  199. purposes, but they will be ignored by the LineEdit functions unless they are
  200. a part of the index key (ie, your primary database index key may be built
  201. on multiple fields.)
  202.  
  203. Now you have:
  204.  
  205. COMPANY.DBF:
  206.  
  207. 1  COMP_NAME, C, 35
  208. 2  COMP_ACCT, C, 10  (INDEX KEY)
  209. ...
  210. ...
  211. ...
  212. n  ...
  213.  
  214. SALENOTE.DBF:
  215.  
  216. 1  COMP_ACCT,  C, 10
  217. 2  SALE_NOTES  C, 80
  218.  
  219. All that remains is to relate the two with an index on SALENOTE.DBF that
  220. will order it by the same key as the primary database.  You are really
  221. just doing what you have always been doing with multiple databases -
  222. creating a relation on index keys.
  223.  
  224. There is one more step however.
  225. -------------------------------
  226.  
  227. The index key of the comment database must match exactly the primary
  228. database index key PLUS the FIRST FOUR BYTES OF THE COMMENT FIELD.
  229.  
  230. See the DEMOS\LINEEDIT\MAIN.PRG for a simple example.
  231.  
  232. It's very simple, really.  Whatever your primary index key is on the
  233. parent database, use it in the comment database ADDING the first four bytes
  234. of the comment field:
  235.  
  236. USE COMPANY.DBF  ALIAS Company  NEW SHARED
  237. INDEX ON Comp_Acct
  238.  
  239. USE SALENOTE.DBF ALIAS SaleNotes NEW SHARED
  240. INDEX ON Comp_Acct+substr(Sale_Notes,1,4)
  241.  
  242. Suppose you use a multiple field index key on the primary database?
  243. No problemo:
  244.  
  245. USE COMPANY.DBF  ALIAS Company  NEW SHARED
  246. INDEX ON Comp_Acct + Comp_Name
  247.  
  248. USE SALENOTE.DBF ALIAS SaleNotes NEW SHARED
  249. INDEX ON Comp_Acct + Comp_Name + substr(Sale_Notes,1,4)
  250.  
  251. The reason for this is that the comment field needs a sequencer in order
  252. to keep the lines in the correct order once the primary index key order
  253. has been satisfied.  Remember, after the index order, the records fall
  254. into natural order (ie, physical record number).  Since comments fields
  255. are commonly recycled from other deleted comments, these record numbers
  256. may fall in almost ANY order.  Thus the sequencer in the index key.
  257.  
  258. Rule of Thumb:  Whatever your parent database index key  use it for
  259. the LineEdit database key, adding only the SUBSTR(field,1,4).  It will
  260. all work fine after that.
  261.  
  262. Comparative Examples:
  263.  
  264. In the following table are the results of some comparisons I made between
  265. a LineEdit database, and a MEMO field.  The DEMO\LINEEDIT\BLITZ.PRG
  266. demo program is the program that I used to do this comparison.
  267.  
  268. It simply adds and deletes the SAME comments, in a (ahem) "random" order.
  269.  
  270. The "random" numbers are selected from a database of numbers
  271. (NUMBERS.DAT) that I entered at random on the keyboard.  This is so I
  272. could always run the exact same pattern several times with different
  273. comment field sizes. I simply bashed away at the keyboard number pad until
  274. I had a "roughly" equivalent number of each digit.  The results are good.
  275. Any given run of the BLITZ.PRG program will almost always report
  276. approximately 50/50 percent adds to deletes, maybe about 4% high on the
  277. ADD side.  See the program source for a fuller explanation of it's operation.
  278.  
  279. In the test, I ran my LineEdit database with a comment field size of 80
  280. bytes, for 100, 500, 1000, and 10000 repetitions.  The program adds the
  281. exact same comment to both the LineEdit comment field and the MEMO field
  282. to ensure an accurate comparrison.  (Would one of you users please write
  283. me and tell me the correct way to spell "comparrison?").
  284.  
  285. The results:
  286.  
  287.                   80 BYTE           MEMOEDIT
  288.                comment field:        Field
  289.  
  290.                    LineEdit         .DBT
  291.        # Reps      File Size        File Size
  292.  
  293.         100         8k                30k
  294.         500        16k               268k
  295.        1000        16k               523k
  296.       10000        16k             5,381k (Yeah!  5+Megabytes!)
  297.  
  298.  
  299. What you will notice from these results is that the memo field behaves as
  300. it always does.  But with the LineEdit database, when the number of adds
  301. and deletes is approximately equal, the LineEdit file reaches a point
  302. of "equallibrium" where the number of available recyclable records is
  303. just sufficient to satisfy the needs of new comments being added.  At
  304. this point, the database, at any given point is about half and half
  305. actual comments and deleted records.
  306.  
  307. This principle does not change when you increase the size of the comment
  308. field.  For instance, in my tests, I also did the same test above with a 300
  309. byte comment field.  The LineEdit database reached equallibrium at 15K again.
  310.  A comment requires about the same storage, no matter how you chop it up!
  311. Since, unlike MEMO fields, no file space is wasted, the program acheives
  312. about the same efficiency.  The advantage to a larger comment field is SPEED.
  313.  The 300 byte comment field stored and retrieved comments MUCH faster than
  314. the 80 byte field because fewer records had to be retrieved to recreate the
  315. comment.  And in the case of this demo program, the 300 byte comment field
  316. was actually MORE efficient both in speed and storage requirements.
  317.  
  318. The most efficient size of the comment field depends much on your needs.
  319. Experimentation will reveal the best size for your needs.  Typically, 80
  320. bytes or more will do fine for "one-liner" comments.
  321.  
  322. NOTE:
  323.  
  324. CAUTION!!!
  325.  
  326. GET IT RIGHT THE FIRST TIME!
  327.  
  328. When determining the size of your comment base comment field, try as best as
  329. you can to get it right the first time.  Changing the structure of the
  330. comment base after it has been put to use is VERY complicated!  It can be
  331. done, but it is time consuming.  If in doubt, go with the HIGHER estimate for
  332. the size of the comment field.
  333.  
  334. Remember, the smaller the length of the comment field, the MORE records
  335. necessary to store a comment.  This means MORE time reading records.  This
  336. can become significant as time passes and the comment base reaches several
  337. thousand records.
  338.  
  339. There is a program "template" that you can use to resize a comment base -
  340. after it has been created and put to use - under DEMOS\UTILS\LE_RESIZ.
  341.  
  342.  
  343. EXAMPLE:
  344.  
  345. USE First ALIAS SmoothStuff NEW SHARED
  346. INDEX ON Da_Field TO First
  347.  
  348. USE second  ALIAS UserComments  NEW SHARED
  349. INDEX ON Da_Field + substr(Notes,1,4) TO Second
  350.  
  351. SELECT SmoothStuff
  352.  
  353. _LineEditPut('519-00-4349','UserComments','NOTES',
  354.  
  355. memoedit(_LineEditGet('519-00-4349', 'UserComments', 'NOTES'),5,1,16,70))
  356.  
  357. Result: memoedit() receives a string from the function to edit and
  358. then rewrites it to the database.  Work area SmoothStuff is still active
  359. in the current work area when the function returns, and the record pointer
  360. of work area UserComments is where it was before the function was called.
  361.  
  362. The above example is a single-line example of how _LineEditGet() can
  363. be literally embedded in the _LineEditPut() function call.  If it
  364. is confusing, it is essentially the same thing as the following, with
  365. an intervening variable used:
  366.  
  367. * 1. get the comment from the database
  368. cString = _LineEditGet('519-00-4349','UserComments','NOTES')
  369.  
  370. * 2. edit it - in this case with memoedit()
  371. cString = memoedit(cString,5,1,16,70)
  372.  
  373. * 3. save it back to the database
  374. _LineEditPut('519-00-4349','UserComments','NOTES',cString)
  375.  
  376.  
  377. On step 1, you could have added the _LineEditSize() check:
  378.  
  379. if _LineEditSize('519-00-4349','UserComments','NOTES') < 64000
  380.     _LineEditGet('519-00-4349','UserComments','NOTES')
  381. endif
  382.  
  383. ******************************************************************************/
  384.